﻿using System;
using System.Linq;
using System.Reflection;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Markup;
using jSLCore.Client.Utility;
using jSLCore.Utility.Extensions;

namespace jSLCore.Client.Presentation.jBinding
{
    /// <summary>
    /// Привязка для чтения
    /// </summary>
    [ContentProperty("Converter")]
    public class jReadOnlyBinding : jConvertableBinding
    {
        public jReadOnlyBinding() : this(string.Empty, string.Empty)
        {
        }

        public jReadOnlyBinding(string elementPropertyPath) : this(elementPropertyPath, string.Empty)
        {
        }

        public jReadOnlyBinding(string elementPropertyPath, string dataContextPropertyPath) : base(elementPropertyPath, dataContextPropertyPath)
        {
        }

        /// <summary>
        /// Механизм чтения данных из источника в цель
        /// </summary>
        internal override void FromData()
        {
            object dataValue;
            try
            {
                dataValue = GetValueForTarget();
            }
            catch (SomethingInvalidException)
            {
                return;// if this happened - there is no any value to set on.
            }
            
            SetValueToTarget(dataValue);
        }

        #region Чтение и запись значения

        public virtual void SetValueToTarget(object dataValue)
        {
            /*try
            {
*/
                if (uiElement != null)
                {
                    try
                    {
                        if ((UIElement is TextBox) && (ElementPropertyPath == "Text") && dataValue == null)
                            dataValue = string.Empty;//'cause text can't set null
                        dataValue = dataValue.CastOrConvert(TargetType);
                    }
                    catch (SomethingInvalidException)
                    {
                        throw new jValidationException(jValidationResultType.Error, "Неверное значение");
                    }

                    
                        if((UIElement is TextBlock || UIElement is TextBox) && ElementPropertyPath=="Text" && dataValue == null )
                        {
                            dataValue = string.Empty;

                        }
                        try
                        {
                            uiElement.SetProperty(ElementPropertyPath, dataValue);
                        }
                        catch (NotImplementedException)
                        {

                        }
                        try
                        {
                            SetTargetValueChanged(dataValue);    
                        }
                        catch (Exception exception)
                        {
                            throw new Exception("Exception occured during FromDataProcessed call. See innerException",exception);
                        }
                        
                    
                    /* catch(TargetInvocationException)
                     {
                         if((UIElement is TextBox) && (ElementPropertyPath=="Text") && dataValue==null)
                         {
                                 //'cause TextBox.Text dons not allow null, but we want it
                         }
                         else
                         {
                             throw;
                         }
                     }*/
                    

                    
                    /*
                }


            }
            catch (Exception)
            {
                //По тактике WPF ничего не делаем*/
            }
        }

        /// <summary>
        /// Mode of syncing within the group
        /// Avoid of recursion
        /// </summary>
        public GroupUpdateMode UpdateGroupMode { get; set; }

        /// <summary>
        /// Invoked when attemt to write target occured
        /// </summary>
        /// <param name="dataValue"></param>
        protected void SetTargetValueChanged(object dataValue)
        {
            InvokeTargetValueChanged(dataValue);

            if (BaseControl != null)
                foreach (var jBindingBase in BaseControl
                    .GetjBindingByParentIsThisAndBindingGroup(BindingGroup)
                    .Where(b=>(b is jReadOnlyBinding) && (((jReadOnlyBinding)b).UpdateGroupMode==GroupUpdateMode.UpdateTargetWhenGroupFromData || ((jReadOnlyBinding)b).UpdateGroupMode==GroupUpdateMode.Both))
                    .ToArray())
                {
                    if(jBindingBase !=this)
                    jBindingBase.UpdateTarget();   
                }
        }

        public event EventHandler<GenericEventArgs<object >> FromDataProcessed;
        public void InvokeTargetValueChanged(object val)
        {
            EventHandler<GenericEventArgs<object >> handler = FromDataProcessed;
            if (handler != null) handler(this, new GenericEventArgs<object>(val));
        }

        /// <summary>
        /// Methods returns the value, which should be setted to the target (After converting). It can throw SomethingInvalidException, if the value cannot be taken (if data context is null, for example)
        /// </summary>
        /// <returns>Methods returns the value, which should be setted to the target (After converting).</returns>
        public virtual object GetValueForTarget()
        {
            if (uiElement != null)
            {
                if (uiElement.DataContext == null) throw new SomethingInvalidException();//we do this to break the FromData process
                    if (uiElement.DataContext.HasProperty(Path))
                    {
                        var dataValue = uiElement.DataContext.GetProperty(Path);

                        dataValue = Convert(dataValue);
                        return dataValue;
                    }
            }
            return null;
        } 
        #endregion

        internal override void ToData()
        {
            //ничего не делаем, мы только читаем
        }
    }
}
